package com.pz.gulimall.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.pz.gulimall.common.constant.ProductConstant;
import com.pz.gulimall.common.exception.BizCodeEnum;
import com.pz.gulimall.common.exception.RRException;
import com.pz.gulimall.common.to.SkuHasStockTo;
import com.pz.gulimall.common.to.es.SkuEsModelTo;
import com.pz.gulimall.common.utils.R;
import com.pz.gulimall.product.entity.*;
import com.pz.gulimall.product.entity.resultMap.SkuInfoResultMap1;
import com.pz.gulimall.product.feign.SearchFeign;
import com.pz.gulimall.product.feign.WareFeign;
import com.pz.gulimall.product.service.*;
import com.pz.gulimall.product.vo.res.ProductInfoResVo;
import com.pz.gulimall.product.vo.res.SkuItemSaleAttr;
import com.pz.gulimall.product.vo.res.SpuItemAttrGroup;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pz.gulimall.common.utils.PageUtils;
import com.pz.gulimall.common.utils.Query;

import com.pz.gulimall.product.dao.SkuInfoDao;

import javax.annotation.Resource;


@Slf4j
@Service("skuInfoService")
public class SkuInfoServiceImpl extends ServiceImpl<SkuInfoDao, SkuInfoEntity> implements SkuInfoService {
    @Autowired
    private SpuInfoService spuInfoService;
    @Autowired
    private SkuImagesService skuImagesService;
    @Autowired
    private AttrGroupService attrGroupService;
    @Autowired
    private ThreadPoolExecutor executor;
    @Resource
    private WareFeign wareFeign;
    @Resource
    private SearchFeign searchFeign;
    @Resource
    private ProductAttrValueService productAttrValueService;
    @Resource
    private AttrService attrService;
    @Resource
    private CategoryService categoryService;
    @Resource
    private BrandService brandService;



    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<SkuInfoEntity> page = this.page(
                new Query<SkuInfoEntity>().getPage(params),
                new QueryWrapper<SkuInfoEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public void saveSkuInfo(SkuInfoEntity skuInfoEntity) {
        this.baseMapper.insert(skuInfoEntity);
    }

    @Override
    public PageUtils queryPageCondition(Map<String, Object> params) {
        QueryWrapper<SkuInfoEntity> queryWrapper = new QueryWrapper<>();
        String key = (String) params.get("key");
        String catelogId = (String) params.get("catelogId");
        String brandId = (String) params.get("brandId");
        String minPrice = (String) params.get("min");
        String maxPrice = (String) params.get("max");
        if(key != null && !key.isEmpty()){
            queryWrapper.and(w->{
               w.eq("sku_id", key).or().like("sku_name",key);
            });
        }
        if(catelogId != null && !catelogId.equals("0")){
            queryWrapper.eq("catalog_id", catelogId);
        }
        if(brandId != null && !brandId.equals("0")){
            queryWrapper.eq("brand_id", brandId);
        }
        if(!StringUtils.isEmpty(minPrice)){
           queryWrapper.ge("price", minPrice);
        }
        if(!StringUtils.isEmpty(maxPrice)){
            try {
                BigDecimal bigDecimal = new BigDecimal(maxPrice);
                if(bigDecimal.compareTo(new BigDecimal("0")) == 1){
                    queryWrapper.le("price", maxPrice);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        IPage<SkuInfoEntity> page = this.page(
                new Query<SkuInfoEntity>().getPage(params),
                queryWrapper
        );
        return new PageUtils(page);
    }

    /**
     * 处理用户客户端搜索商品详情页面的数据
     * @param skuId
     * @return
     */
    @Override
    @Cacheable(value = "productInfo", key = "#root.args[0]")
    public ProductInfoResVo queryProductInfo(Long skuId) throws ExecutionException, InterruptedException {
        ProductInfoResVo resVo = new ProductInfoResVo();
        SkuInfoEntity skuInfoEntity;
        SpuInfoEntity spuInfoEntity;
        List<SkuImagesEntity> imags;
        List<SpuItemAttrGroup> baseAttrs;
        List<SkuItemSaleAttr> saleAttrs;

        CompletableFuture<SkuInfoEntity> future1 = CompletableFuture.supplyAsync(() -> getById(skuId), executor);
        CompletableFuture<List<SkuImagesEntity>> future3 = CompletableFuture.supplyAsync(() -> skuImagesService.list(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId)), executor);

        CompletableFuture<SpuInfoEntity> future2 = future1.thenApplyAsync((res) -> spuInfoService.getById(res.getSpuId()), executor);
        CompletableFuture<List<SpuItemAttrGroup>> future4 = future2.thenApplyAsync((res) -> attrGroupService.getProductBaseAttr(res.getCatalogId(), res.getId()), executor);
        CompletableFuture<List<SkuItemSaleAttr>> future5 = future2.thenApplyAsync((res) -> this.baseMapper.querySaleAttrs(res.getId()), executor);

        CompletableFuture.allOf(future1, future2, future3, future4, future5);

        skuInfoEntity = future1.get();
        spuInfoEntity = future2.get();
        imags = future3.get();
        baseAttrs = future4.get();
        saleAttrs = future5.get();

        resVo.setSkuInfo(skuInfoEntity);
        resVo.setSpuInfo(spuInfoEntity);
        resVo.setImags(imags);
        //组合基本属性
        resVo.setBaseAttrs(baseAttrs);
        //组合销售属性
        resVo.setSaleAttrs(saleAttrs);

        return resVo;
    }

    //更新Es中的数据模型
    @Override
    public void updateSkuEsModel(Long spuId) {
        //拼装上传ES的数据模型
        List<SkuEsModelTo> skuEsModels = new ArrayList<>();
        List<SkuInfoEntity> skuInfoEntities = this.list(new QueryWrapper<SkuInfoEntity>().eq("spu_id", spuId));

        //检索出当前 spu 所对应的基本属性 (attr_value)
        List<ProductAttrValueEntity> baseAttrs = productAttrValueService.list(new QueryWrapper<ProductAttrValueEntity>().eq("spu_id", spuId));
        // 得到 当前商品基本属性的 attr_id
        List<Long> attrIds = baseAttrs.stream().map(ProductAttrValueEntity::getAttrId)
                .collect(Collectors.toList());
        //查出 可以被检索的 attrId
        List<Long> searchAttrs = attrService.listSearchAttrs(attrIds);
        //将可以被检索的 attrId 去重搜集
        Set<Long> searchAttrSet = new HashSet<>(searchAttrs);
        //得到 各个可以被检索 的属性
        List<SkuEsModelTo.Attr> attrs = baseAttrs.stream().map(attr -> {
            SkuEsModelTo.Attr attr1 = new SkuEsModelTo.Attr();
            BeanUtils.copyProperties(attr, attr1);
            return attr1;
        }).filter(filter -> {
            //过滤出可以被 搜搜的 attr_value
            return searchAttrSet.contains(filter.getAttrId());
        }).collect(Collectors.toList());

        //搜集当前 spu 对应的所有 skuId
        List<Long> skuIds = skuInfoEntities.stream().map(SkuInfoEntity::getSkuId).collect(Collectors.toList());
        //询问是否有库存
        Map<Long, Boolean> hasStock = null;
        try {
            R res = wareFeign.hasStock(skuIds);
            if(res.getCode() == 0){
                List<SkuHasStockTo> hasStockTos = res.getDataArray(SkuHasStockTo.class);
                hasStock = hasStockTos.stream().collect(Collectors.toMap(SkuHasStockTo::getSkuId, SkuHasStockTo::getHasStock));
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("库存服务查询异常: 原因{}", e.getMessage());
            throw new RRException(BizCodeEnum.PRODUCT_RPC_WARE.getMsg()+e.getMessage(), BizCodeEnum.PRODUCT_RPC_WARE.getCode());
        }

        Map<Long, Boolean> finalHasStock = hasStock;
        skuInfoEntities.forEach(skuInfo -> {
            SkuEsModelTo skuEsModelTo = new SkuEsModelTo();
            BeanUtils.copyProperties(skuInfo, skuEsModelTo);
            skuEsModelTo.setSkuPrice(skuInfo.getPrice());
            skuEsModelTo.setSkuImg(skuInfo.getSkuDefaultImg());
            //TODO 热度评分规则
            skuEsModelTo.setHotScore(0L);

            BrandEntity brand = brandService.getById(skuEsModelTo.getBrandId());
            skuEsModelTo.setBrandName(brand.getName());
            skuEsModelTo.setBrandImg(brand.getLogo());
            skuEsModelTo.setCatalogName(categoryService.getById(skuEsModelTo.getCatalogId()).getName());
            if(finalHasStock != null){
                skuEsModelTo.setHasStock(finalHasStock.get(skuEsModelTo.getSkuId()));
            }else {
                skuEsModelTo.setHasStock(true);
            }

            skuEsModelTo.setAttrs(attrs);
            skuEsModels.add(skuEsModelTo);
        });

        //调用 es 服务，上传数据
        try {
            R saveAns = searchFeign.saveSku(skuEsModels);
            if(saveAns.getCode() != 0){
                //远程服务业务异常
                //TODO 重复调用，接口幂等性
                log.error("上架失败,搜索服务消息--{}", saveAns.getMsg());
                throw new RRException(saveAns.getMsg(), saveAns.getCode());
            }
        } catch (Exception e){
            log.error("远程调用search服务失败--{}", e.getMessage());
            e.printStackTrace();
            throw new RRException(BizCodeEnum.PRODUCT_RPC_SEARCH.getMsg()+e.getMessage(), BizCodeEnum.PRODUCT_RPC_SEARCH.getCode());
        }
    }

    @Override
    public List<SkuInfoResultMap1> queryBatchBySkuIds(List<Long> skuIds) {
        return this.baseMapper.querySkuInfoBatch(skuIds);
    }
}